package cn.lg.soar.redis.aspect.clusterlock;

import cn.lg.soar.redis.annotations.ClusterReadLock;
import cn.lg.soar.redis.annotations.ClusterWriteLock;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.springframework.core.annotation.Order;
import org.springframework.util.ConcurrentReferenceHashMap;

import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Author: luguoxiang
 * @Date: 2021-05-18 15:41
 * @Description: 分布式读写锁aop实现
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
@Aspect
@Order(0)
public class ClusterReadLockAspect {

    private final Map<ClusterReadLock, IConfig> RCACHE = new ConcurrentReferenceHashMap<>();
    private final Map<ClusterWriteLock, IConfig> WCACHE = new ConcurrentReferenceHashMap<>();

    @Resource
    private Redisson redisson;

    @Around("@annotation(clusterLock)")
    public Object around(ProceedingJoinPoint point, ClusterReadLock clusterLock) throws Throwable {

        // 获取配置
        IConfig config = RCACHE.get(clusterLock);
        if (null == config) {
            config = this.initRConfig(point, clusterLock);
        }

        RReadWriteLock readWriteLock = this.redisson.getReadWriteLock(config.lockName(point));
        RLock lock = readWriteLock.readLock();
        try{
            // 获取锁
            if (lock.tryLock(clusterLock.waitTime(), clusterLock.leaseTime(), TimeUnit.MILLISECONDS)) {
                return point.proceed();
            }
        } finally {
            // 释放锁
            if (lock.isLocked()) {
                lock.unlock();
            }
        }
        throw new RuntimeException(clusterLock.message());
    }

    @Around("@annotation(clusterLock)")
    public Object around(ProceedingJoinPoint point, ClusterWriteLock clusterLock) throws Throwable {

        // 获取配置
        IConfig config = WCACHE.get(clusterLock);
        if (null == config) {
            config = this.initWConfig(point, clusterLock);
        }

        RReadWriteLock readWriteLock = this.redisson.getReadWriteLock(config.lockName(point));
        RLock lock = readWriteLock.writeLock();

        try{
            // 获取锁
            if (lock.tryLock(clusterLock.waitTime(), clusterLock.leaseTime(), TimeUnit.MILLISECONDS)) {
                return point.proceed();
            }
        } finally {
            // 释放锁
            if (lock.isLocked()) {
                lock.unlock();
            }
        }
        throw new RuntimeException(clusterLock.message());
    }

    /**
     * 读锁配置
     * @param point
     * @param clusterLock
     * @return
     */
    private synchronized IConfig initRConfig(ProceedingJoinPoint point, ClusterReadLock clusterLock) {
        IConfig config = RCACHE.get(clusterLock);
        if (config != null) {
            return config;
        }
        // 锁前缀
        String prefix = "lock:" + clusterLock.value();
        // 构建配置
        config = IConfig.build(point, prefix, clusterLock.key());
        RCACHE.put(clusterLock, config);
        return config;
    }

    /**
     * 写锁配置
     * @param point
     * @param clusterLock
     * @return
     */
    private synchronized IConfig initWConfig(ProceedingJoinPoint point, ClusterWriteLock clusterLock) {
        IConfig config = WCACHE.get(clusterLock);
        if (config != null) {
            return config;
        }
        // 锁前缀
        String prefix = "lock:" + clusterLock.value();
        // 构建配置
        config = IConfig.build(point, prefix, clusterLock.key());
        WCACHE.put(clusterLock, config);
        return config;
    }

}
